package org.dromara.generator.service;


import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.generator.constant.GenConstants;
import org.dromara.generator.domain.GenTable;
import org.dromara.generator.domain.GenTableColumn;
import org.dromara.generator.mapper.GenTableColumnMapper;
import org.dromara.generator.mapper.GenTableMapper;
import org.dromara.generator.util.VelocityInitializer;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.StringWriter;
import java.time.LocalDateTime;
import java.util.*;

@Service
@Slf4j
public class GeneratorService {

    @Resource
    GenTableMapper genTableMapper;
    @Resource
    GenTableColumnMapper genTableColumnMapper;



    public void generate(List<TableConfig> configList) {
        if (CollectionUtils.isNotEmpty(configList)) {
            configList.stream().forEach((config)-> {
                generate(config);
            });
        }
    }


    public void generate(TableConfig config) {

        if (config == null
            || StringUtils.isEmpty(config.getSchema())
            || StringUtils.isEmpty(config.getTable())
            || StringUtils.isEmpty(config.getModule())
            || StringUtils.isEmpty(config.getModuleName())
            || StringUtils.isEmpty(config.getDomainPackage())
            || StringUtils.isEmpty(config.getServicePackage())
            || StringUtils.isEmpty(config.getControllerPackage())
            || StringUtils.isEmpty(config.getDomainClass())
            || StringUtils.isEmpty(config.getMapperPackage())) {
            log.error("配置信息不能为空");
            return;
        }
        log.info("=================开始生成代码: 表={} ===============", config.getTable());
        GenTable genTable = genTableMapper.selectOne(
            new LambdaQueryWrapper<GenTable>()
                .eq(GenTable::getTableSchema, config.getSchema())
                .eq(GenTable::getTableName, config.getTable()));

        if (genTable == null) {
            log.error("指定表 {} 不存在，请在数据库中创建对应表 !!");
            return;
        }
        Map<String, ColumnConfig> configMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(config.getColumns())) {
            config.getColumns().stream().forEach((column)->{
               configMap.put(column.getColumn(), column);
            });
        }
        List<GenTableColumn> genTableColumns = genTableColumnMapper.selectList(
            new LambdaQueryWrapper<GenTableColumn>()
                .eq(GenTableColumn::getTableSchema, config.getSchema())
                .eq(GenTableColumn::getTableName, config.getTable())
        );

        List<Column> columns = new ArrayList<>();
        List<String> imports = new ArrayList<>();
        Column primaryColumn = null;
        if (CollectionUtils.isNotEmpty(genTableColumns)) {
            for (GenTableColumn genTableColumn : genTableColumns) {
                if (GenConstants.base_columns.contains(genTableColumn.getColumnName())) {
                    continue;
                }
                Column column = new Column();
                column.setColumn(genTableColumn.getColumnName());
                ColumnConfig columnConfig = configMap.get(genTableColumn.getColumnName());
                if (columnConfig != null) {
                    if (StringUtils.isNotEmpty(columnConfig.getPropertyClass())) {
                        String[] classSplit = columnConfig.getPropertyClass().split("\\.");
                        column.setType(classSplit[classSplit.length - 1]);
                        if (!GenConstants.not_import_class.contains(columnConfig.getPropertyClass())) {
                            imports.add(columnConfig.getPropertyClass());
                        }
                    } else if (columnConfig.getPropertyType() != null) {
                        if (ColumnConfig.PropertyType.BOOLEAN.equals(columnConfig.getPropertyType())) {
                            column.setType("Boolean");
                        } else if (ColumnConfig.PropertyType.DATE.equals(columnConfig.getPropertyType())) {
                            column.setType("LocalDate");
                            imports.add("java.time.LocalDate");
                        } else if (ColumnConfig.PropertyType.DATETIME.equals(columnConfig.getPropertyType())) {
                            column.setType("LocalDateTime");
                            imports.add("java.time.LocalDateTime");
                        } else if (ColumnConfig.PropertyType.DECIMAL.equals(columnConfig.getPropertyType())) {
                            column.setType("BigDecimal");
                            imports.add("java.math.BigDecimal");
                        } else if (ColumnConfig.PropertyType.TIME.equals(columnConfig.getPropertyType())) {
                            column.setType("LocalTime");
                            imports.add("java.time.LocalTime");
                        } else if (ColumnConfig.PropertyType.LONG.equals(columnConfig.getPropertyType())) {
                            column.setType("Long");
                        } else if (ColumnConfig.PropertyType.INT.equals(columnConfig.getPropertyType())) {
                            column.setType("Integer");
                        } else {
                            column.setType("String");
                        }
                    }
                    column.setProperty(columnConfig.getProperty());
                    column.setComment(columnConfig.getComment());
                    column.setVueType(columnConfig.getVueType());
                    if (columnConfig.getRequire() != null) {
                        column.setRequire(columnConfig.getRequire());
                    }
                    if (columnConfig.getSearch() != null) {
                        column.setSearch(columnConfig.getSearch());
                    }
                    if (columnConfig.getInsert() != null) {
                        column.setInsert(columnConfig.getInsert());
                    }
                    if (columnConfig.getEdit() != null) {
                        column.setEdit(columnConfig.getEdit());
                    }
                }

                if (StringUtils.isEmpty(column.getType())) {
                    if ("decimal".equals(genTableColumn.getDataType())) {
                        column.setType("BigDecimal");
                        imports.add("java.math.BigDecimal");
                    } else if ("datetime".equals(genTableColumn.getDataType()) || "timestamp".equals(genTableColumn.getDataType())) {
                        column.setType("LocalDateTime");
                        imports.add("java.time.LocalDateTime");
                    } else if ("tinyint".equals(genTableColumn.getDataType())) {
                        column.setType("Boolean");
                    } else if ("bigint".equals(genTableColumn.getDataType())) {
                        column.setType("Long");
                    } else if ("int".equals(genTableColumn.getDataType())) {
                        column.setType("Integer");
                    }else {
                        column.setType("String");
                    }
                }
                if (StringUtils.isEmpty(column.getProperty())) {
                    column.setProperty(StrUtil.toCamelCase(genTableColumn.getColumnName()));
                }
                if (StringUtils.isEmpty(column.getComment())) {
                    column.setComment(genTableColumn.getComment());
                }

                if (column.getVueType() == null) {
                    if ("tinyint".equals(genTableColumn.getDataType())) {
                        column.setVueType(ColumnConfig.VueType.SWITCH);
                    } else if ("datetime".equals(genTableColumn.getDataType()) || "timestamp".equals(genTableColumn.getDataType())) {
                        column.setVueType(ColumnConfig.VueType.DATETIME);
                    } else if ("text".equals(genTableColumn.getDataType())) {
                        column.setVueType(ColumnConfig.VueType.TEXTAREA);
                    } else {
                        column.setVueType(ColumnConfig.VueType.STRING);
                    }
                }

                column.setSort(genTableColumn.getOrdinalPosition());
                if ("PRI".equals(genTableColumn.getColumnKey())) {
                    primaryColumn = column;
                } else {
                    columns.add(column);
                }
            }
        }
        columns.sort((item1, item2)->{
            return item1.getSort() - item2.getSort();
        });


        // 初始化模板引擎
        VelocityInitializer.initVelocity();

        VelocityContext velocityContext = new VelocityContext();
        velocityContext.put("tableName", config.getTable());
        velocityContext.put("module", config.getModule());
        velocityContext.put("moduleName", config.getModuleName());
        velocityContext.put("domainPackage", config.getDomainPackage());
        velocityContext.put("servicePackage", config.getServicePackage());
        velocityContext.put("functionName", StringUtils.isEmpty(config.getFunctionName()) ? genTable.getComment() : config.getFunctionName());
        velocityContext.put("controllerPackage", config.getControllerPackage());
        velocityContext.put("domainClass", config.getDomainClass());
        velocityContext.put("mapperPackage", config.getMapperPackage());
        velocityContext.put("domainName", StringUtils.isEmpty(config.getDomainName()) ? StrUtil.toCamelCase(genTable.getTableName()) : config.getDomainName());
        velocityContext.put("primaryColumn", primaryColumn);
        velocityContext.put("columns", columns);
        velocityContext.put("imports", imports);
        velocityContext.put("datetime", LocalDateTimeUtil.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));
        velocityContext.put("customPagination", config.getCustomPagination() == null ? false : config.getCustomPagination());


        List<String> templates = getTemplateList();
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
            tpl.merge(velocityContext, sw);
            try {
                String path = getGenPath(template, velocityContext);
                FileUtils.writeUtf8String(sw.toString(), path);
            } catch (Exception e) {
                throw new ServiceException("渲染模板失败，表名：" + config.getTable());
            }
        }


        log.info("=================代码生成完成 表={} ===============", config.getTable());
    }




    /**
     * 获取模板信息
     *
     * @return 模板列表
     */
    public static List<String> getTemplateList() {
        List<String> templates = new ArrayList<>();
        templates.add("vms/java/domain.java.vm");
        templates.add("vms/java/query.java.vm");
        templates.add("vms/java/mapper.java.vm");
        templates.add("vms/java/service.java.vm");
        templates.add("vms/java/controller.java.vm");
        templates.add("vms/vue/api.ts.vm");
        templates.add("vms/vue/index.vue.vm");
        templates.add("vms/xml/mapper.xml.vm");
        templates.add("vms/sql/sql.vm");

        return templates;
    }


    /**
     * 获取代码生成地址
     *
     * @param template 模板文件路径
     * @return 生成地址
     */
    public static String getGenPath(String template,VelocityContext context) {
        return System.getProperty("user.dir") + File.separator + "generate" + File.separator + getFileName(template, context);
    }


    public static String getFileName(String template,VelocityContext context) {
        String domainName = (String)context.get("domainName");
        String domainClass = (String)context.get("domainClass");
        String module = (String)context.get("module");
        String fileName = null;
        if (template.contains("domain.java.vm")) {
            fileName = StringUtils.format("java/{}.java", domainClass);
        } else if (template.contains("query.java.vm")) {
            fileName = StringUtils.format("java/{}Query.java", domainClass);
        } else if (template.contains("mapper.java.vm")) {
            fileName = StringUtils.format("java/{}Mapper.java", domainClass);
        } else  if (template.contains("service.java.vm")) {
            fileName = StringUtils.format("java/{}Service.java", domainClass);
        } else  if (template.contains("controller.java.vm")) {
            fileName = StringUtils.format("java/{}Controller.java", domainClass);
        } else  if (template.contains("api.ts.vm")) {
            fileName = StringUtils.format("vue/api/{}/{}/index.ts", module, domainName);
        } else  if (template.contains("index.vue.vm")) {
            fileName = StringUtils.format("vue/views/{}/{}/index.vue", module, domainName);
        } else  if (template.contains("mapper.xml.vm")) {
            fileName = StringUtils.format("xml/{}Mapper.xml",  domainClass);
        } else if (template.contains("sql.vm")) {
            fileName = StringUtils.format("sql/{}_menu.sql", domainClass);
        }
        return fileName;
    }



}
